package com.sl.ms.carriage.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sl.ms.base.api.common.AreaFeign;
import com.sl.ms.base.domain.base.AreaDto;
import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.CarriageDTO;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.domain.enums.EconomicRegionEnum;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.enums.CarriageExceptionEnum;
import com.sl.ms.carriage.handler.CarriageChainHandler;
import com.sl.ms.carriage.mapper.CarriageMapper;
import com.sl.ms.carriage.service.CarriageService;
import com.sl.ms.carriage.utils.CarriageUtils;
import com.sl.transport.common.exception.SLException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class CarriageServiceImpl extends ServiceImpl<CarriageMapper, CarriageEntity> implements CarriageService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 新增/修改运费模板
     *
     * @param carriageDto 新增/修改运费对象
     *                    必填字段：templateType、transportType
     *                    更新时传入id字段
     */
    @Override
    public CarriageDTO saveOrUpdate(CarriageDTO carriageDto) {
        // 1. 查询模板是否存在 查询条件：模板类型 运送类型 是否在表中已存在
        LambdaQueryWrapper<CarriageEntity> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery.eq(CarriageEntity::getTemplateType, carriageDto.getTemplateType());
        lambdaQuery.eq(CarriageEntity::getTransportType, carriageDto.getTransportType());
        lambdaQuery.ne(ObjectUtil.isNotEmpty(carriageDto.getId()), CarriageEntity::getId, carriageDto.getId());
        List<CarriageEntity> carriageEntityList = super.list(lambdaQuery);
        // 2. 不存在则保存
        if (CollUtil.isEmpty(carriageEntityList)) {
            return this.saveOrUpdateCarriage(carriageDto);
        }
        // 3. 判断模板类型是否为经济区互寄 如果不为经济区互寄则抛异常
        if (ObjectUtil.notEqual(carriageDto.getTemplateType(), CarriageConstant.ECONOMIC_ZONE)) {
            throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
        }
        // 4. 如果为经济区互寄，判断与前端传的associatedCityList是否有交集
        //   4.1 获取已存在的模板
        List<String> associateCityList = carriageEntityList.stream().map(CarriageEntity::getAssociatedCity)
                .map(city -> StrUtil.splitToArray(city, ",")) // 以逗号进行分隔，形成String数组
                .flatMap(Arrays::stream) // 将String数组展开为具体的String
                .collect(Collectors.toList());
        //   4.2 如果有交集，则抛异常
        Collection<String> intersection = CollUtil.intersection(associateCityList, carriageDto.getAssociatedCityList());
        if (CollUtil.isNotEmpty(intersection)) {
            throw new SLException(CarriageExceptionEnum.ECONOMIC_ZONE_CITY_REPEAT);
        }
        // 4. 如果没交集，保存
        return this.saveOrUpdateCarriage(carriageDto);
    }

    /**
     * 保存/修改运费模板 carriageDto会存有id
     *
     * @param carriageDto
     * @return
     */
    private CarriageDTO saveOrUpdateCarriage(CarriageDTO carriageDto) {
        CarriageEntity carriageEntity = CarriageUtils.toEntity(carriageDto);
        super.saveOrUpdate(carriageEntity);
        return CarriageUtils.toDTO(carriageEntity);
    }

    /**
     * 获取全部运费模板
     *
     * @return 运费模板对象列表
     */
    @Override
    public List<CarriageDTO> findAll() {
        // 1. 封装查询条件
        LambdaQueryWrapper<CarriageEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.orderByDesc(CarriageEntity::getCreated);
        // 2. 查询
        List<CarriageEntity> carriageEntityList = super.list(lambdaQueryWrapper);
        // 3. 转换为DTO集合返回
        List<CarriageDTO> carriageDTOList = carriageEntityList.stream().map(CarriageUtils::toDTO).collect(Collectors.toList());
        return carriageDTOList;
    }

    @Resource
    private CarriageChainHandler chainHandler;

    /**
     * 运费计算 加入缓存
     */
    @Override
    public CarriageDTO compute(WaybillDTO waybillDTO) {
        String key = getKey(waybillDTO.getSenderCityId(), waybillDTO.getReceiverCityId());
        String json = stringRedisTemplate.opsForValue().get(key);
        CarriageEntity carriageEntity = null;
        if (StrUtil.isNotBlank(json)) {
            carriageEntity = JSONUtil.toBean(json, CarriageEntity.class);
        }
        if (ObjectUtil.isEmpty(carriageEntity)) {
            carriageEntity = chainHandler.findCarriage(waybillDTO);
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(carriageEntity));
        }
        // 2. 计算计费重量 最小重量为1kg
        double computeWeight = getComputeWeight(waybillDTO, carriageEntity);
        // 3. 计算运费 = 首重 + (计费重量- 1 ) * 续重价格
        double price = carriageEntity.getFirstWeight() + (computeWeight - 1) * carriageEntity.getContinuousWeight();
        // 保留1位小数
        price = NumberUtil.round(price, 1).doubleValue();
        // 4. 返回DTO
        CarriageDTO carriageDTO = CarriageUtils.toDTO(carriageEntity);
        carriageDTO.setExpense(price);
        carriageDTO.setComputeWeight(computeWeight);
        return carriageDTO;
    }

    /**
     * 获取缓存Key carriage:senderCityId_receiverCityId
     */
    private String getKey(Long senderCityId, Long receiverCityId) {
        return String.format(CarriageConstant.CACHE_PREFIX, senderCityId + "_" + receiverCityId);
    }

    /**
     * 运费计算
     *
     * @param waybillDTO 运费计算对象
     * @return 运费模板对象，不仅包含模板数据还包含：computeWeight、expense 字段
     */
    /*@Override
    public CarriageDTO compute(WaybillDTO waybillDTO) {
        // 1. 根据前端传来的两个城市id 查询模板
        //CarriageEntity carriageEntity = findCarriage(waybillDTO);
        CarriageEntity carriageEntity = chainHandler.findCarriage(waybillDTO);
        // 2. 计算计费重量 最小重量为1kg
        double computeWeight = getComputeWeight(waybillDTO, carriageEntity);
        // 3. 计算运费 = 首重 + (计费重量- 1 ) * 续重价格
        double price = carriageEntity.getFirstWeight() + (computeWeight - 1) * carriageEntity.getContinuousWeight();
        // 保留1位小数
        price = NumberUtil.round(price, 1).doubleValue();
        // 4. 返回DTO
        CarriageDTO carriageDTO = CarriageUtils.toDTO(carriageEntity);
        carriageDTO.setExpense(price);
        carriageDTO.setComputeWeight(computeWeight);
        return carriageDTO;
    }*/

    /**
     * 计算计费重量 最小重量为1kg
     */
    private double getComputeWeight(WaybillDTO waybillDTO, CarriageEntity carriageEntity) {
        // 1. 计算体积
        Integer volume = waybillDTO.getVolume();
        if (ObjectUtil.isEmpty(volume)) {
            try {
                // 长×宽×高
                volume = waybillDTO.getMeasureLong() * waybillDTO.getMeasureWidth() * waybillDTO.getMeasureHigh();
            } catch (Exception e) {
                // 出现异常时直接设为0
                volume = 0;
            }
        }
        // 2. 计算体积质量 体积/轻抛系数 保留1位小数
        double volumeWeight = NumberUtil.div(volume, carriageEntity.getLightThrowingCoefficient(), 1).doubleValue();
        // 3. 取最大的质量作为重量
        double computeWeight = NumberUtil.max(volumeWeight, waybillDTO.getWeight());
        // 4. 计算小数点
        //   4.1 不足1kg按1kg算
        if (computeWeight <= 1) {
            return 1;
        }
        //   4.2 不足10kg 保留1位小数
        if (computeWeight <= 10) {
            return computeWeight; //刚才计算的已经是保留1位小数的了
        }
        //   4.3 大于等于100 四舍五入 取整
        if (computeWeight >= 100) {
            return NumberUtil.round(computeWeight, 0).doubleValue();
        }
        //   4.4 10kg~100kg 小数点小于0.5时，按0.5算  大于等于0.5时，加1
        // 向下取整
        int integer = NumberUtil.round(computeWeight, 0, RoundingMode.DOWN).intValue();
        double sub = NumberUtil.sub(computeWeight, integer);
        if (sub == 0) {
            return integer;
        }
        if (sub <= 0.5) {
            return NumberUtil.add(integer, 0.5);
        }
        return NumberUtil.add(integer, 1);
    }

    @Resource
    private AreaFeign areaFeign;

    /**
     * 查询模板
     */
    private CarriageEntity findCarriage(WaybillDTO waybillDTO) {
        // 1. 是否同城
        Long senderCityId = waybillDTO.getSenderCityId();
        Long receiverCityId = waybillDTO.getReceiverCityId();
        if (ObjectUtil.equal(senderCityId, receiverCityId)) {
            CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_CITY);
            if (ObjectUtil.isNotEmpty(carriageEntity)) {
                return carriageEntity;
            }
        }
        // 2. 是否同省
        Long senderProvinceId = areaFeign.get(senderCityId).getParentId();
        Long receiverProvinceId = areaFeign.get(receiverCityId).getParentId();
        if (ObjectUtil.equal(senderProvinceId, receiverProvinceId)) {
            CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_PROVINCE);
            if (ObjectUtil.isNotEmpty(carriageEntity)) {
                return carriageEntity;
            }
        }
        // 3. 是否为经济特区互寄
        CarriageEntity carriageEntity = findEconomicCarriage(senderProvinceId, receiverProvinceId);
        if (ObjectUtil.isNotEmpty(carriageEntity)) {
            return carriageEntity;
        }
        // 4. 是否跨省
        carriageEntity = findByTemplateType(CarriageConstant.TRANS_PROVINCE);
        if (ObjectUtil.isNotEmpty(carriageEntity)) {
            return carriageEntity;
        }
        // 5. 抛异常
        throw new SLException(CarriageExceptionEnum.NOT_FOUND);
    }

    /**
     * 判断是否为经济区互寄
     */
    private CarriageEntity findEconomicCarriage(Long senderProvinceId, Long receiverProvinceId) {
        // 1. 获取所有经济区枚举
        LinkedHashMap<String, EconomicRegionEnum> enumMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
        // 2. 遍历枚举类，判断是否都在枚举中
        EconomicRegionEnum economicRegionEnum = null;
        for (EconomicRegionEnum regionEnum : enumMap.values()) {
            boolean isContainAll = ArrayUtil.containsAll(regionEnum.getValue(), senderProvinceId, receiverProvinceId);
            if (isContainAll) {
                economicRegionEnum = regionEnum;
                break;
            }
        }
        if (ObjectUtil.isEmpty(economicRegionEnum)) {
            return null;
        }
        LambdaQueryWrapper<CarriageEntity> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE);
        lambdaQueryWrapper.eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST);
        lambdaQueryWrapper.eq(CarriageEntity::getAssociatedCity, economicRegionEnum.getCode());
        return super.getOne(lambdaQueryWrapper);
    }

    /**
     * 根据模板类型查询模板，经济区互寄不通过该方法查询模板
     *
     * @param templateType 模板类型：1-同城寄，2-省内寄，4-跨省
     * @return 运费模板
     */
    @Override
    public CarriageEntity findByTemplateType(Integer templateType) {
        LambdaQueryWrapper<CarriageEntity> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(CarriageEntity::getTemplateType, templateType);
        lambdaQueryWrapper.eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST);
        return super.getOne(lambdaQueryWrapper);
    }

}
